package org.examples.java.util.regex;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.text.AttributeSet;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;

// TODO Store regex examples in xml
// TODO Manage multiple reg expressions. Add navigation tree on the left
// TODO Add link to regex javadoc
/**
 * Simple GUI that allows to tune regular expressions.
 * 
 * @author yevhenii.dermenzhii
 */
public class RegexTuner extends JFrame
{
    private static final long serialVersionUID = 1L;
    private static final int MAIN_WIDTH = 1024;
    private static final int MAIN_HEGHT = 768;

    private static final AttributeSet backgroundAttr = StyleContext.getDefaultStyleContext().addAttribute(
        SimpleAttributeSet.EMPTY, StyleConstants.Background, Color.LIGHT_GRAY );
    private static final AttributeSet foregroundAttr = StyleContext.getDefaultStyleContext().addAttribute(
        SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.BLUE );

    private JTextField regexTextField;
    private JTextPane inputTextPane;
    private JTextArea outputTextArea;
    private JCheckBox multilineCheckbox;
    private JCheckBox dotallCheckbox;
    private DefaultStyledDocument inputTextDoc;

    String content = "[BCS_Voice]\n"
            + "        file.name=/iscp/msedata/cdrdata/default/BCS_Voice.log\n"
            + "        file.check.interval=30s\n"
            + "        disk.full.interval=60s\n"
            + "        delete.oldest.file=true\n"
            + "        roll.type=time\n"
            + "        roll.size.max=5MB\n"
            + "        roll.time.period=1HR\n"
            + "        roll.max.index=3\n"
            + "        \n"
            + "        data.layout= SUBSCRIBERID, \\\n"
            + "                     TRANSACTIONIDENTIFIER:SEQUENCEID, \\\n"
            + "                     APPLICATIONID:REASON, \\\n"
            + "                     SERVICEPROVIDER:PROFILE:ACCOUNTSTATE:ACCESSID:GROUPID:RATEPLAN|SECONDARYKEY,\\\n"
            + "                     TRANSACTIONSTART:ANSWERTIME:TIMEZONE|DURATION, \\\n"
            + "                     TARIFFGROUPCLASS:RPUSED:MEDIATYPE|VLRNUMBER:CELLID|OTHERPARTYNUM:OTHERPARTYNOA:PARTYNUM, \\\n"
            + "                     COSTDETAIL, \\\n" + "                     RATERULES, \\\n"
            + "                     MAINBALANCEAMOUNT:CEDATE, \\\n" + "                     USAGEDELTAS, \\\n"
            + "                     FEATUREFLAGS, \\\n" + "                     DISCOUNTRULES, \\\n"
            + "                     SPONSORSHIPREVENUE \n" + "\n"
            + "        data.class=com.telcordia.cvas.mse.bcs.bb.edr.BcsEdrVoice\n"
            + "        archive.agents=ftpagent\n" + "\n" + "    ########";

    public static void main( String[] args )
    {
        RegexTuner regexTuner = new RegexTuner();
        regexTuner.setVisible( true );
    }

    public RegexTuner() throws HeadlessException
    {
        super( "Regex Tuner" );
        centerPosition();
        setDefaultCloseOperation( EXIT_ON_CLOSE );

        setLayout( new BorderLayout( 5, 5 ) );

        JPanel regexPanel = new JPanel( new BorderLayout() );
        regexPanel.add( new JLabel( "Regex: (press Enter to parse)" ), BorderLayout.NORTH );
        regexTextField = new JTextField( "([\\w\\.]+)=((.)*)" );
        regexTextField.setFont( new Font( Font.MONOSPACED, Font.BOLD, 14 ) );
        regexPanel.add( regexTextField, BorderLayout.CENTER );

        JPanel optionsPanel = new JPanel( new FlowLayout() );
        multilineCheckbox = new JCheckBox( "Pattern.MULTILINE", true );
        dotallCheckbox = new JCheckBox( "Pattern.DOTALL", false );
        optionsPanel.add( multilineCheckbox );
        optionsPanel.add( dotallCheckbox );
        regexPanel.add( optionsPanel, BorderLayout.SOUTH );

        JPanel textPanel = new JPanel( new BorderLayout() );
        textPanel.add( new JLabel( "Text to parse: (add text below)" ), BorderLayout.NORTH );
        inputTextDoc = new DefaultStyledDocument();
        inputTextPane = new JTextPane( inputTextDoc );
        inputTextPane.setText( content );
        inputTextPane.setFont( new Font( Font.MONOSPACED, Font.PLAIN, 12 ) );
        // inputTextArea.setPreferredSize( new Dimension( 0, 300 ) );
        JScrollPane inputScrollPane = new JScrollPane( inputTextPane );
        inputScrollPane.setPreferredSize( new Dimension( 0, 300 ) );
        textPanel.add( inputScrollPane, BorderLayout.CENTER );

        JPanel outputPanel = new JPanel( new BorderLayout() );
        outputPanel.add( new JLabel( "Output:" ), BorderLayout.NORTH );
        outputTextArea = new JTextArea();
        outputTextArea.setFont( new Font( Font.MONOSPACED, Font.PLAIN, 12 ) );
        outputPanel.add( new JScrollPane( outputTextArea ), BorderLayout.CENTER );

        JSplitPane splitPane = new JSplitPane( JSplitPane.VERTICAL_SPLIT, textPanel, outputPanel );

        add( regexPanel, BorderLayout.NORTH );
        add( splitPane, BorderLayout.CENTER );

        regexTextField.addActionListener( new ActionListener()
        {

            @Override
            public void actionPerformed( ActionEvent e )
            {
                parseRegex();
            }
        } );
    }

    private void parseRegex()
    {
        String regex = regexTextField.getText();
        String text = inputTextPane.getText();

        StringBuilder output = new StringBuilder();
        output.append( String.format( "regex: %s\n", regex ) );
        output.append( "Parsing " + new Date() + "...\n" );

        List<Match> matches = new ArrayList<Match>();
        try
        {
            Pattern pattern = Pattern.compile( regex, getFlags() );
            Matcher matcher = pattern.matcher( text );
            while ( matcher.find() )
            {
                for ( int i = 0; i < matcher.groupCount(); i++ )
                {
                    output.append( String.format( "Group %d: '%s'\n", i, matcher.group( i ) ) );
                    matches.add( new Match( i, matcher.start( i ), matcher.end( i ) ) );
                }
            }
        }
        catch ( PatternSyntaxException e )
        {
            output.append( "Wrong regex synthax: " + e.getMessage() );
        }
        redrawOutput( output, matches );
    }

    private void redrawOutput( StringBuilder output, List<Match> matches )
    {
        String input = inputTextPane.getText();
        inputTextDoc = new DefaultStyledDocument();
        inputTextPane.setDocument( inputTextDoc );
        inputTextPane.setText( input );
        for ( Match match : matches )
        {
            AttributeSet attributeSet = (match.group == 0) ? backgroundAttr : foregroundAttr;
            inputTextDoc.setCharacterAttributes( match.start, match.end - match.start, attributeSet, false );
        }
        inputTextPane.repaint();

        outputTextArea.setText( output.toString() );
        outputTextArea.repaint();
    }

    private int getFlags()
    {
        int flags = 0;
        if ( multilineCheckbox.isSelected() )
            flags |= Pattern.MULTILINE;
        if ( dotallCheckbox.isSelected() )
            flags |= Pattern.DOTALL;
        return flags;
    }

    private void centerPosition()
    {
        Point center = GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint();
        setBounds( center.x - MAIN_WIDTH / 2, center.y - MAIN_HEGHT / 2, MAIN_WIDTH, MAIN_HEGHT );
    }

    private class Match
    {
        private final int group;
        private final int start;
        private final int end;

        Match( int group, int start, int end )
        {
            this.group = group;
            this.start = start;
            this.end = end;
        }
    }

}
